
import { DeformTimeline } from "./MeshDeformTimeline";

const { ccclass, property, requireComponent } = cc._decorator;
interface deformItem {
    time?: number,
    offset?: number,
    vertices?: number[],
    curve?: number[] | number | string,
    c2?: number,
    c3?: number,
    c4?: number,
}

export interface DeformMap {
    [name: string]: deformItem[]
}

export interface animation {
    timeline: DeformTimeline,
    duration: number
}

export interface animationMap {
    [name: string]: animation,
}

@ccclass
@requireComponent(cc.Sprite)
export default class MeshDeform extends cc.Component {
    private vertexes: number[] = [
        352.97, 0,
        -259.03, 0,
        -259.03, 512,
        252.97, 512,
        -12.75, 245.6
    ];
    public deformTimeline: DeformTimeline = null;
    public duration: number = 0;
    public animations: animationMap = null;

    start() {
        this.animations = {};
    }

    private _animationConfig: DeformMap = null;
    /**
     * 加载变形动画配置
     * @param obj 
     */
    public loadAnimationsConfig(obj: DeformMap) {
        this.animations = {};
        this._animationConfig = obj;
    }

    private isLoop: boolean = false;
    private _curAnimationTime: number = 0;
    private _curentAnimation: animation = null;
    public playAction(actionName: string, isLoop: boolean = false) {
        if (!this._animationConfig || !this._animationConfig[actionName]) {
            cc.error("please call [loadAnimationsConfig] to load animations config");
            return;
        }
        //解析对应actionName数据
        if (!this.animations[actionName]) {
            this.animations[actionName] = this.parseDeformAction(this._animationConfig[actionName]);
        }
        this._curentAnimation = this.animations[actionName];
        //更新数据
        this.isLoop = isLoop;
        //初始化动画状态
        this._curAnimationTime = 0;
    }

    private parseDeformAction(objs: deformItem[]) {
        let sprite = this.node.getComponent(cc.Sprite);
        let timeline = new DeformTimeline(objs.length, sprite);
        let deformLength = sprite.spriteFrame.vertices.x.length*2;
        let frameIndex = 0;
        let duration = 0;
        for (let j = 0, len = objs.length; j < len; j++) {
            let valueMap = objs[j];
            let deform = null;
            let verticesValue = valueMap.vertices ? valueMap.vertices : null;
            if (verticesValue == null) {
                deform = this.vertexes;
            } else {
                deform = Array(deformLength).fill(0);
                let start = valueMap.offset ? valueMap.offset : 0;
                this.arrayCopy(verticesValue, 0, deform, start, verticesValue.length);
                for (let i = 0; i < deformLength; i++) {
                    deform[i] += this.vertexes[i];
                }
            }
            timeline.setFrame(frameIndex, valueMap.time || 0, deform);
            this.readCurve(valueMap, timeline, frameIndex);
            frameIndex++;
            duration = Math.max(duration, valueMap.time || 0);
        }
        return { timeline, duration };
    }
    protected update(dt: number): void {
        if (this._curentAnimation) {
            this._curentAnimation.timeline.apply(this._curAnimationTime, 1, 1);
            this._curAnimationTime += dt;
            if (this._curAnimationTime >= this._curentAnimation.duration) {
                this._curAnimationTime = 0;
                if (!this.isLoop) {
                    this._curentAnimation = null;
                }
            }
        }
    }
    /**
     * 复制数组
     * @param source 
     * @param sourceStart 
     * @param dest 
     * @param destStart 
     * @param numElements 
     */
    private arrayCopy = function (source, sourceStart, dest, destStart, numElements) {
        for (var i = sourceStart, j = destStart; i < sourceStart + numElements; i++, j++) {
            dest[j] = source[i];
        }
    }

    //设置缓动曲线
    private readCurve(map: deformItem, timeline: DeformTimeline, frameIndex: number) {
        var curve = map.curve;
        if (!curve)
            return;
        if (curve == "stepped") {
            timeline.setStepped(frameIndex);
        } else if (Object.prototype.toString.call(curve) === '[object Array]') {
            timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]);
        } else {
            timeline.setCurve(frameIndex, curve as number, map.c2 || 0, map.c3 || 1, map.c4 || 1);
        }
    }
}
